#define _GNU_SOURCE 1
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<poll.h>
#include<fcntl.h>
#define  BUF_SIZE 1024
#define USER_LIMIT 5
#define FD_LIMIT 65535
struct client_data
{
  sockaddr_in address;
    char* write_buf;
    char buf[BUF_SIZE];
};
int setnonblocking(int fd)
{
  int old_option=fcntl(fd,F_GETFL);
    int new_option=old_option|O_NONBLOCK;
    fcntl(fd,F_SETFL,new_option);
    return old_option;
}
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
     printf("usage :%s ip  port\n",basename(argv[0])); 
        exit(0);
    }
    const char* ip=argv[1];
    int port=atoi(argv[2]);
    int ret=0;
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family=AF_INET;
    inet_pton(AF_INET,ip,&address.sin_addr);
    address.sin_port=htons(port);
    int listen_sock=socket(PF_INET,SOCK_STREAM,0);
    assert(listen_sock>=0);
    ret=bind(listen_sock,(struct sockaddr*)&address,sizeof(address));
    assert(ret!=-1);
    ret=listen(listen_sock,5);
    assert(ret!=-1);
    client_data* users= new client_data[FD_LIMIT];
    pollfd fds[USER_LIMIT+1];
    int user_count=0;
    for(int i=1;i<=USER_LIMIT;i++)
    {
       fds[i].fd=-1;
        fds[i].events=0;
    }
    fds[0].fd=listen_sock;
    fds[0].events=POLLIN | POLLERR;
    fds[0].revents=0;
      while(1)
    {
        ret=poll(fds,user_count+1,-1);
        if(ret<0)
        {
            printf("poll failure\n");
                    break;
        }// if ret<0
          for(int i=0;i<user_count+1;i++)
        {
            if((fds[i].fd==listen_sock) && ( fds[i].revents & POLLIN))
            {
                struct sockaddr_in client;
                socklen_t len=sizeof(client);
                int connect_fd=accept(listen_sock,(struct sockaddr*)&client,&len);
                if(connect_fd<0)
                {
                    printf("accept\n");
                        continue;
                }
                if(user_count>=USER_LIMIT)
                {
                    const char* info="too many user\n";
                    printf("%s\n",info);
                    send(connect_fd,info,strlen(info),0);
                    close(connect_fd);
                    continue;
                }
                user_count++;
                users[connect_fd].address=client;
                setnonblocking(connect_fd);
                fds[user_count].fd=connect_fd;
                fds[user_count].events=POLLIN|POLLRDHUP|POLLERR;
                fds[user_count].revents=0;
                printf("connect a new client,taltol %d users\n",user_count);
            }//if
       else if(fds[i].revents & POLLERR)
               {
                   printf("get an error from %d\n",fds[i].fd);
                    char errors[100];
                   memset(errors,'\0',100);
                   socklen_t length=sizeof(errors);
                   if(getsockopt(fds[i].fd,SOL_SOCKET,SO_ERROR,&errors,&length)<0)
                   {
                       printf("getsockopt\n");
                   }
                   continue;
                }//esle if
                else if(fds[i].revents & POLLRDHUP)
                {
                    users[fds[i].fd]=users[fds[user_count].fd];
                    close(fds[i].fd);
                    fds[i]=fds[user_count];
                    i--;
                    user_count--;
                    printf("a client left\n");
                }//esle if
                    else if(fds[i].revents & POLLIN)
                    {
                        int connect_fd=fds[i].fd;
                        memset(users[connect_fd].buf,'\0',BUF_SIZE);
                        ret=recv(connect_fd,users[connect_fd].buf,BUF_SIZE-1,0);
 printf("get [%d] bytes of client data:%s from: %d\n",ret,users[connect_fd].buf,connect_fd);
                        if(ret<0)
                 {
                        if(errno!=EAGAIN)
                        {
                            close(connect_fd);
                            users[fds[i].fd]=users[fds[user_count].fd];
                            fds[i]=fds[user_count];
                            i--;
                            user_count--;
                        }

                    }//esle if 
                    else if(ret==0)
                    { 
                       printf("timeout\n");
                    }
                    else
                    {
                        for(int j=1;j<=user_count;++j)
                        {
                            if(fds[j].fd==connect_fd)
                            {
                                continue;
                            }
                            fds[j].events|=~POLLIN;
                            fds[j].events|=POLLOUT;
                            users[fds[j].fd].write_buf=users[connect_fd].buf;
                        }
                    }//ret>0
                }

else if(fds[i].revents & POLLOUT)
{
    int connect_fd=fds[i].fd;
    if(!users[connect_fd].write_buf)
    {
        continue;
    }
    ret =send(connect_fd,users[connect_fd].write_buf,strlen(users[connect_fd].write_buf),0);
     fds[i].events|=~POLLOUT;
    fds[i].events|=POLLIN;
}
        }
    }
delete [] users;
close(listen_sock);
return 0;
}
